package com.googlecode.cswish.struts;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.servlet.ServletContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import com.googlecode.cswish.Config;
import com.googlecode.cswish.struts.hibernate.HibernateConfiguration;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.struts.spring.generator.GeneratorHelper;
import com.googlecode.cswish.util.FrameConstant;
import com.googlecode.cswish.util.Functions;
import com.googlecode.cswish.util.OgnlExUtil;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.LocalizedTextUtil;

/**
 * Prepare the environment
 * 
 * @author Jerry.Feng 2008-10-19
 */
@Service
public class StartupManager {

	private static final Log logger = LogFactory.getLog(StartupManager.class);
	
	@Inject
	private ServletContext servletContext;
	
	@Resource
	private ObjectFactory objectFactory;
	
	@Resource
	private Config config;
	
	public void startup(Container container, BeanDefinitionRegistry appContext) {
		Config cfg = null;
		try {
			// force the factory to load 'Config' if there's definition
			cfg = (Config) objectFactory.getAutowireCapableBeanFactory()
				.getBean(Config.class.getName());
		} catch (Exception ex) {
		}
		if (cfg == null){
			// TODO: actionPackages from web.xml
//			String actionPackages = null;
//			if (actionPackages != null) {
//				Config.getInstance().setAppPackage(actionPackages);
//			}
			
			if (logger.isInfoEnabled()) {
				String message = "********** INFO LOAD CSWISH CONFIGURATION **********\n" +
		            "    Can't find Config from ApplicationContext.xml, the default Config is:\n" + config.toString() + 
		            "    You can add the following to ApplicationContext.xml: \n" +
		            "        <bean class=\"com.googlecode.cswish.Config\" scope=\"singleton\">\n" +
		            "            <property name=\"appPackage\" value=\"your.project.application.package\" />\n" +
		            "            ...\n" +
		            "        </bean>";
				logger.info(message);
			}
		}
		
		LocalizedTextUtil.addDefaultResourceBundle(Config.class.getPackage().getName() + ".i18n");
		
		// give the application a chance to override the default bean definition
//		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(appContext);
//		reader.loadBeanDefinitions("classpath:/applicationContext-" + config.getProjectName() + ".xml");
		
		HibernateConfiguration hibernateConfiguration = objectFactory.getBean(HibernateConfiguration.class, true);
		
		// force HibernateConfiguration to initilize
		hibernateConfiguration.getClassMapping("");
		
		// initialize the static variable
		objectFactory.getBean(GeneratorHelper.class, true);
		// initialize the inject property
		objectFactory.getBean(OgnlExUtil.class, true);
		
		// prepare a global functions
		Functions fn = objectFactory.getBean(Functions.class, true);
		servletContext.setAttribute(FrameConstant.STACK_FUNCTION, fn);
		
		
		try {
			if (hibernateConfiguration.isJPA) {
				EntityManagerFactory emf = hibernateConfiguration.getEntityManagerFactory();
				EntityManager em = emf.createEntityManager();
//				em.setFlushMode(javax.persistence.FlushModeType.AUTO);
				TransactionSynchronizationManager.bindResource(emf,
						new EntityManagerHolder(em));
			} else {
				SessionFactory sessionFactory = hibernateConfiguration.getSessionFactory();
				Session session = SessionFactoryUtils.getSession(sessionFactory,
						true);
				session.setFlushMode(FlushMode.MANUAL);
				TransactionSynchronizationManager.bindResource(sessionFactory,
						new SessionHolder(session));
			}
		
			IStartupManager[] startupManagers = objectFactory.getBeans(IStartupManager.class, true);
			if (startupManagers != null) {
				for (IStartupManager startupManager : startupManagers) {
					startupManager.startup(servletContext);
				}
			}
		} catch (Throwable t) {
			logger.error("Transaction exception", t);
		} finally {
			if (hibernateConfiguration.isJPA) {
				EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager
						.unbindResource(hibernateConfiguration.getEntityManagerFactory());
				EntityManagerFactoryUtils.closeEntityManager(emHolder
						.getEntityManager());
			} else {
				SessionFactory sessionFactory = hibernateConfiguration.getSessionFactory();
				SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager
						.unbindResource(sessionFactory);
				SessionFactoryUtils.releaseSession(sessionHolder.getSession(),
						sessionFactory);
			}
		}
		
	}
}
